;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  A WWVB Time Code Processor - John A. Magliacane, KD2BD   ;
;            Project started on December 26, 2004           ;
;                   Last update: May 24, 2013               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;     To program: picprog -q --burn --erase -i wwvb.hex     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;      A 10 Hz clock is applied to PORT A Bit 1 (Pin 18)    ;
;      WWVB time code is applied to PORT A Bit 0 (Pin 17)   ;
;      BPSK is applied to PORT A Bit 5 (Pin 4)              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	LIST p=16F88
	RADIX decimal
	INCLUDE "p16f88.inc"

	errorlevel	-302	; no "register in bank 0" warnings

	__CONFIG _CONFIG1, _INTRC_IO & _WDT_OFF & _PWRTE_ON & _CP_OFF & _DEBUG_OFF & _LVP_OFF & _MCLR_OFF

	__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF

	CBLOCK	20H
		pwm
		one
		zero
		bpsk:2
		wwvb:2
		marker
		signal
		counter
		seconds
		zero_one
		one_marker
		zero_marker
		marker_count
		wwvb_ut1
		wwvb_year
		wwvb_hours
		wwvb_minutes
		wwvb_daynumH
		wwvb_daynumL
		wwvb_leapsec
		rtc_daynumH
		rtc_daynumL
		rtc_ut1
		rtc_year
		rtc_hours
		rtc_minutes
		rtc_seconds
		rtc_status
		rtc_day
		rtc_month
		rtc_leapsec
		rtc_dayofweek
		AccValue
		TempYear
		NumH
		NumL
		TenK
		Thou
		Hund
		Tens
		Ones
		ctr
		mod
		arg_mod
		temp
		tmpctr
		delayctr
		countdown
		TuningTimer
		Q1_LO
		Q1_HI
		Q2_LO
		Q2_HI
		R_LO
		R_HI
		Jan:2
		Feb:2
		Mar:2
		Apr:2
		May:2
		Jun:2
		Jul:2
		Aug:2
		Sep:2
		Oct:2
		Nov:2
		Dec:2
	ENDC

	CBLOCK	70H	; This RAM is mirrored across every bank.
		_w
		_status
		_pclath
		rscount
		HH
		hh
		MM
		mm
		SS
		ss
		MO
		mo
		DD
		dd
		YY
		yy
	ENDC

	ORG 0

	goto Main

	ORG 4

 ;;;;;;;;;;;;;;;;;; 
;; Interrupt code ;;
 ;;;;;;;;;;;;;;;;;; 

Interrupt:

	movwf	_w		; Save STATUS, W, and PCLATH registers
	swapf	STATUS, w	; in RAM before executing interrupt code.
	clrf	STATUS
	movwf	_status
	movf	PCLATH, w
	movwf	_pclath
	clrf	PCLATH

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	btfss	PIR1, TXIF	; Is USART TX buffer empty?
	goto	endint		; No.  TX buffer is full.  Exit routine.

				; Is there anything left to transmit?
	movf	rscount, f	; rscount --> rscount (to set Z flag)
	btfss	STATUS, Z	; Zero bytes left?
	goto	$+4		; No.  Execute code to transmit a character.

	movlw	D'0'		; Null character
	movwf	TXREG		; Transmit it to clear TXIF
	goto	endint		; Exit routine
	
	;;; rscount!=0  Execute appropriate code ;;;

	movf	rscount, w	; rscount --> w
	sublw	D'19'		; (19 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 19?
	goto	tx_HH		; Yes.  Transmit HH

	movf	rscount, w	; rscount --> w
	sublw	D'18'		; (18 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 18?
	goto	tx_hh		; Yes.  Transmit hh

	movf	rscount, w	; rscount --> w
	sublw	D'17'		; (17 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 17?
	goto	tx_col		; Yes.  Transmit ':'

	movf	rscount, w	; rscount --> w
	sublw	D'16'		; (16 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 16?
	goto	tx_MM		; Yes.  Transmit MM

	movf	rscount, w	; rscount --> w
	sublw	D'15'		; (15 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 15?
	goto	tx_mm		; Yes.  Transmit mm

	movf	rscount, w	; rscount --> w
	sublw	D'14'		; (14 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 14?
	goto	tx_col		; Yes.  Transmit ':'

	movf	rscount, w	; rscount --> w
	sublw	D'13'		; (13 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 13?
	goto	tx_SS		; Yes.  Transmit SS

	movf	rscount, w	; rscount --> w
	sublw	D'12'		; (12 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 12?
	goto	tx_ss		; Yes.  Transmit ss

	movf	rscount, w	; rscount --> w
	sublw	D'11'		; (11 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 11?
	goto	tx_sp		; Yes.  Transmit <space>

	movf	rscount, w	; rscount --> w
	sublw	D'10'		; (10 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 10?
	goto	tx_MO		; Yes.  Transmit MO

	movf	rscount, w	; rscount --> w
	sublw	D'9'		; (9 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 9?
	goto	tx_mo		; Yes.  Transmit mo

	movf	rscount, w	; rscount --> w
	sublw	D'8'		; (8 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 8?
	goto	tx_sl		; Yes.  Transmit '/'

	movf	rscount, w	; rscount --> w
	sublw	D'7'		; (7 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 7?
	goto	tx_DD		; Yes.  Transmit DD

	movf	rscount, w	; rscount --> w
	sublw	D'6'		; (6 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 6?
	goto	tx_dd		; Yes.  Transmit dd

	movf	rscount, w	; rscount --> w
	sublw	D'5'		; (5 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 5?
	goto	tx_sl		; Yes.  Transmit '/'

	movf	rscount, w	; rscount --> w
	sublw	D'4'		; (4 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 4?
	goto	tx_YY		; Yes.  Transmit YY

	movf	rscount, w	; rscount --> w
	sublw	D'3'		; (3 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 2?
	goto	tx_yy		; Yes.  Transmit yy

	movf	rscount, w	; rscount --> w
	sublw	D'2'		; (2 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 2?
	goto	tx_cr		; Yes.  Transmit carriage return character

	movf	rscount, w	; rscount --> w
	sublw	D'1'		; (1 - w) --> w
	btfsc	STATUS, Z	; Does rscount == 1?
	goto	tx_lf		; Yes.  Transmit line-feed character
	goto	endint		; We shouldn't get here.

tx_HH:	movf	HH, w		; HH --> w
	iorlw	D'48'		; Convert HH to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_hh:	movf	hh, w		; hh --> w
	iorlw	D'48'		; Convert hh to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_col:	movlw	':'		; Send ':'
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_MM:	movf	MM, w		; MM --> w
	iorlw	D'48'		; Convert MM to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit
	
tx_mm:	movf	mm, w		; mm --> w
	iorlw	D'48'		; Convert mm to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_SS:	movf	SS, w		; SS --> w
	iorlw	D'48'		; Convert SS to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_ss:	movf	ss, w		; ss --> w
	iorlw	D'48'		; Convert ss to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_sp:	movlw	D'32'		; Space between time and date
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_DD:	movf	DD, w		; DD --> w
	iorlw	D'48'		; Convert DD to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_dd:	movf	dd, w		; dd --> w
	iorlw	D'48'		; Convert dd to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_sl:	movlw	'/'		; Slash character
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_MO:	movf	MO, w		; MO --> w
	iorlw	D'48'		; Convert dd to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_mo:	movf	mo, w		; mo --> w
	iorlw	D'48'		; Convert dd to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_YY:	movf	YY, w		; YY --> w
	iorlw	D'48'		; Convert YY to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_yy:	movf	yy, w		; yy --> w
	iorlw	D'48'		; Convert yy to ASCII
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_cr:
	movlw	D'13'		; Carriage return character
	movwf	TXREG		; send it out
	decf	rscount, f	; rscount--
	goto	endint		; exit

tx_lf:	movlw	'\n'		; Line feed -- The last character to xmit
	movwf	TXREG		; send it out
	clrf	rscount		; 0 --> rscount

	bsf	STATUS, RP0		; Switch to Bank 1
	bcf	PIE1^0x080, TXIE	; Disable RS232 TX Interrupts
	bcf	STATUS, RP0		; Back to Bank 0

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

endint:
	movf	_pclath, w	; Restore STATUS, W, and PCLATH
	movwf	PCLATH		; registers to original values.
	swapf	_status, w
	movwf	STATUS
	swapf	_w, f
	swapf	_w, w

	retfie			; Return and enable interrupts


;;;;;;;;;;;;;; rtc_status bits ;;;;;;;;;;;;;;;;;
;
;	0 : rtc clock set flag
;	1 : rtc clock verified flag
;	2 : PWM framing bit success flag
;	3 : "minor success" flag
;	4 : "Time was set inside the +45 degree phase shift period" flag
;	5 : leap year flag (1=leap year)
;	6 :
;	7 : LCD display "setup" flag
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
                    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Main program.  Where the magic begins... ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Main:
	clrf	rtc_status	; Initialize real-time clock status
	clrf	TuningTimer
	clrf	rscount
	clrf	bpsk
	clrf	bpsk+1
	movlw	D'59'		; The nominal number of seconds after
	movwf	seconds		; which the rtc_minute is rolled over.
				; This is changed to 60 during a positive
				; leap second period or 58 during a negative
				; leap second period in leapsec_check().

	;;; Initialize ports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	bcf	STATUS, RP0	; Select Bank 0 to access PORT A
	bcf	STATUS, RP1

	clrf	PORTA		; Initialize PORT A

	movlw	B'10010001'	; Select AN2 and turn ON A/D
	movwf	ADCON0		; Conversion time is 8uS at 4 MHz

	bsf	RCSTA, SPEN	; 1 --> SPEN (Turns on USART)

	bsf	STATUS, RP0	; Select Bank 1 for ANSEL, TRISA, OSCCON, SPBRG

	movlw	B'00001100'	; PORTA bits 2 and 3 are analog inputs
	movwf	ANSEL		; Configure ANSEL Register

	clrf	TRISA^0x080
	movlw	B'00101111'	; PORT A bits 0 --> 3 and 5 are inputs.
	movwf	TRISA^0x080

	movlw	B'01100000'	; 4 MHz Internal Oscillator

	movwf	OSCCON^0x080

	movlw	B'10100000'	; Right justified A/D with ref on AN3
	movwf	ADCON1^0x080

	bcf	STATUS, RP0	; Select Bank 0 to access PORT B

	clrf	PORTB		; Initialize PORT B

	bsf	STATUS, RP0	; Select Bank 1 to access TRISB

	clrf	TRISB^0x080
	movlw	0x00		; All PORT B bits are outputs
	movwf	TRISB^0x080

	;;; Initialize Serial TX Port ;;;;;;;;;;;;;;;;;;;;;;;;;

	movlw	D'12'			; 19.2 kbps using INTRC at 4 MHz 
	movwf	SPBRG^0x080

	bcf	TXSTA^0x080, SYNC	; 0 --> SYNC (async mode)
	bsf	TXSTA^0x080, BRGH	; 1 --> BRGH (high baud rate)
	bcf	TXSTA^0x080, TX9	; 0 --> TX9 (8-bit transmission)
	bsf	TXSTA^0x080, TXEN	; 1 --> TXEnable (TXEN)
	
	bcf	STATUS, RP0		; Back to Bank 0

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	bcf	PORTA, 4	; Ensure the 74HC390 reset pulse is LOW

	call	LCD_Init	; Initialize the LCD display

	call	msg1		; Display firmware version
	call	pwm_sync	; Sync to the start of PWM bit

	bsf	INTCON, GIE	; 1 --> GIE (Enable global interrupts)
	bsf	INTCON, PEIE	; 1 --> PEIE (Enable peripheral interrupts)

Bad_Loop:
	bcf	rtc_status, 0	; Clear "clock set" bit
	call	frame_sync	; Sync to the start of time code frame

Good_Loop:
	call	decode_frame	; Decode the frame and test the exit conditions
	btfsc	rtc_status, 3	; Did we exit successfully?  ("minor success")
	goto	Good_Loop	; Yes? -->  Decoding is properly synced.
	btfsc	rtc_status, 1	; Has RTC been verified?
	goto	Good_Loop	; Yes!  --> Decoding is properly synced.
	goto	Bad_Loop	; No? -->  Re-sync to the start of the frame

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine: Take the MOD 7 of "arg_mod" and return the result in "mod".  ;
; Preset (or clear) variable "mod" as needed before invoking this routine. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mod7:
	movlw	D'2'
	btfsc	arg_mod, 7
	addwf	mod, f		; mod+=2
	btfsc	arg_mod, 6
	incf	mod, f		; mod++
	movlw	D'4'
	btfsc	arg_mod, 5
	addwf	mod, f		; mod+=4
	movlw	D'2'
	btfsc	arg_mod, 4
	addwf	mod, f		; mod+=2
	btfsc	arg_mod, 3
	incf	mod, f		; mod++
	movlw	D'7'		; 7 --> w
	andwf	arg_mod, f	; (arg_mod & w) --> arg_mod
	movf	arg_mod, w	; arg_mod --> w
	addwf	mod, f		; (arg_mod + mod) --> mod
modA:	movlw	D'7'
	subwf	mod, w		; (mod - 7) --> w
	btfsc	STATUS, C
	goto	modB		; no carry, therefore result >=0
	movf	mod, w		; mod --> w
	return

modB:	movwf	mod		; w --> mod
	goto	modA

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine - Daynum (0-365 + leap) to calendar (month/day/day of week) ;
; routine.  This routine takes rtc_daynumL, rtc_daynumH, and rtc_year    ;
; and returns rtc_month, rtc_day, and rtc_dayofweek.  It is used for     ;
; displaying date information on LCD display and outputting via RS232.   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Daynum2Cal:

; Initialize variables used for holding day/month calendar information.

	clrf	Jan+1
	movlw	D'31'
	movwf	Jan	; 31 days --> Jan
	
	clrf	Feb+1
	movlw	D'59'
	movwf	Feb	; 59 days --> Feb
	
	clrf	Mar+1
	movlw	D'90'
	movwf	Mar	; 90 days --> Mar
	
	clrf	Apr+1
	movlw	D'120'
	movwf	Apr	; 120 days --> Apr
	
	clrf	May+1
	movlw	D'151'
	movwf	May	; 151 days --> May
	
	clrf	Jun+1
	movlw	D'181'
	movwf	Jun	; 181 days --> Jun
	
	clrf	Jul+1
	movlw	D'212'
	movwf	Jul	; 212 days --> Jul
	
	clrf	Aug+1
	movlw	D'243'
	movwf	Aug	; 243 days --> Aug
	
	movlw	D'1'	; Set high-order byte
	movwf	Sep+1	; for the remaining months
	movwf	Oct+1	; since the number of days is beyond 8-bits
	movwf	Nov+1
	movwf	Dec+1

	movlw	D'17'
	movwf	Sep	; 273 days --> Sep
	
	movlw	D'48'
	movwf	Oct	; 304 days --> Oct
	
	movlw	D'78'
	movwf	Nov	; 334 days --> Nov
	
	movlw	D'109'
	movwf	Dec	; 365 days --> Dec

	;;; Leap year detection and compensation ;;;

	movf	rtc_year, w	; rtc_year --> w
	andlw	D'3'		; (w & 3) --> w
	btfss	STATUS, Z	; A zero status means it's a leap year
	goto	GetMonthAndDay
	bsf	rtc_status, 5	; Set leap year flag
	incf	Feb, f		; Bump day numbers
	incf	Mar, f		; beyond February
	incf	Apr, f
	incf	May, f
	incf	Jun, f
	incf	Jul, f
	incf	Aug, f
	incf	Sep, f
	incf	Oct, f
	incf	Nov, f
	incf	Dec, f

GetMonthAndDay:

; Determine the current month and day based on rtc_daynumH and rtc_daynumL

	movf	Jan, w
	movwf	Q1_LO
	movf	Jan+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto	$+6
	movlw	D'1'
	movwf	rtc_month	; month = January
	movf	rtc_daynumL, w
	movwf	rtc_day	
	
	goto	GetDayOfWeek
	
	movf	Feb, w
	movwf	Q1_LO
	movf	Feb+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'2'
	movwf	rtc_month	; February
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Jan, w		; rtc_day = (rtc_daynum - days thru Jan.)
	movwf	Q2_LO
	movf	Jan+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Mar, w
	movwf	Q1_LO
	movf	Mar+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'3'
	movwf	rtc_month	; March
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Feb, w		; rtc_day = (rtc_daynum - days thru Feb.)
	movwf	Q2_LO
	movf	Feb+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Apr, w
	movwf	Q1_LO
	movf	Apr+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'4'
	movwf	rtc_month	; April
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Mar, w		; rtc_day = (rtc_daynum - days thru Mar.)
	movwf	Q2_LO
	movf	Mar+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	May, w
	movwf	Q1_LO
	movf	May+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'5'
	movwf	rtc_month	; May
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Apr, w		; rtc_day = (rtc_daynum - days thru Apr.)
	movwf	Q2_LO
	movf	Apr+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Jun, w
	movwf	Q1_LO
	movf	Jun+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'6'
	movwf	rtc_month	; June
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	May, w		; rtc_day = (rtc_daynum - days thru May)
	movwf	Q2_LO
	movf	May+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Jul, w
	movwf	Q1_LO
	movf	Jul+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'7'
	movwf	rtc_month	; July
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Jun, w		; rtc_day = (rtc_daynum - days thru Jun.)
	movwf	Q2_LO
	movf	Jun+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Aug, w
	movwf	Q1_LO
	movf	Aug+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'8'
	movwf	rtc_month	; August
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Jul, w		; rtc_day = (rtc_daynum - days thru Jul.)
	movwf	Q2_LO
	movf	Jul+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Sep, w
	movwf	Q1_LO
	movf	Sep+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'9'
	movwf	rtc_month	; September
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Aug, w		; rtc_day = (rtc_daynum - days thru Aug.)
	movwf	Q2_LO
	movf	Aug+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Oct, w
	movwf	Q1_LO
	movf	Oct+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'10'
	movwf	rtc_month	; October
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Sep, w		; rtc_day = (rtc_daynum - days thru Sep.)
	movwf	Q2_LO
	movf	Sep+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Nov, w
	movwf	Q1_LO
	movf	Nov+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'11'
	movwf	rtc_month	; November
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Oct, w		; rtc_day = (rtc_daynum - days thru Oct.)
	movwf	Q2_LO
	movf	Oct+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
	goto	GetDayOfWeek
	
	movf	Dec, w
	movwf	Q1_LO
	movf	Dec+1, w
	movwf	Q1_HI
	movf	rtc_daynumL, w
	movwf	Q2_LO
	movf	rtc_daynumH, w
	movwf	Q2_HI
	call	Sub16
	btfsc	R_HI, 7
	goto 	$+15
	movlw	D'12'
	movwf	rtc_month	; December
	movf	rtc_daynumL, w
	movwf	Q1_LO
	movf	rtc_daynumH, w
	movwf	Q1_HI
	movf	Nov, w		; rtc_day = (rtc_daynum - days thru Nov.)
	movwf	Q2_LO
	movf	Nov+1, w
	movwf	Q2_HI
	call	Sub16
	movf	R_LO, w
	movwf	rtc_day
	
GetDayOfWeek:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This code is based on using December 31, 1995 as a reference epoch     ;
; (day number 0) since this date is a Sunday and is not a leap year.     ;
; Since we're past 2000, and since WWVB sends only the last two digits   ;
; of the current year in its broadcasts, this code only uses the last    ;
; two digits of the year in its calculations.  Day number 1 (01-Jan-96)  ;
; is, therefore, represented as year -4 (252).  This algorithm is very   ;
; loosely based on Microchip's Technical Brief TB028.                    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	clrf	mod		; 0 --> mod
	movlw	D'4'		; 4 --> w
	btfsc	rtc_daynumH, 0	; if we're beyond day number 255,
	movwf	mod		; preset mod to 4, else, preset to 0
	movf	rtc_daynumL, w	; rtc_daynumL --> w
	movwf	arg_mod		; w --> arg_mod
	call	mod7		; return modulus of rtc_daynumL in w
	movwf	AccValue	; w --> AccValue
	movlw	D'252'		; 1996 = -4 (252) referenced to 2000.
	movwf	TempYear	; Assign this to TempYear.

weekloop:
	incf	AccValue, f	; AccValue++
	movf	TempYear, w	; TempYear --> w
	andlw	D'3'		; (w & 3) --> w
	btfsc	STATUS, Z	; A zero result implies...
	incf	AccValue, f	; TempYear is a leap year
	incf	TempYear, f	; TempYear++
	movf	rtc_year, w	; Current Year --> w
	subwf	TempYear, w	; (TempYear - w) --> w
	btfss	STATUS, Z	; Tempyear == Current Year?
	goto 	weekloop	; Loop again until we reach the current year

	clrf	mod
	movf	AccValue, w	; AccValue --> w
	movwf	arg_mod		; w --> arg_mod
	call	mod7		; Take modulus of arg_mod, get result in w
	movwf	rtc_dayofweek	; 0=Sun, 1=Mon, 2=Tues, 3=Wed, etc.

	return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This subroutine determines if the current day is the last day of the  ;
; current month.  It is used to determine if a leap second needs to be  ;
; inserted into the running RTC at 23:59:59 on the current date.  When  ;
; adding leap seconds into the UTC time code, preference is given first ;
; to December or June, then to March or September.  Months are checked  ;
; in this order to save some processing time.                           ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

EndOfMonth:

	btfss	rtc_daynumH, 0	
	goto	matchL			; test rtc_daynumL only

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Dec, w			; (Dec  - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of December

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Sep, w			; (Sep - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of September

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Nov, w			; (Nov - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of November

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Oct, w			; (Oct - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of October
	goto	no_month_match		; No match

matchL:

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Jun, w			; (Jun - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of June

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Mar, w			; (Mar - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of March

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Aug, w			; (Aug - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of August

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Jul, w			; (Jul - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of July

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	May, w			; (May - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of May

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Apr, w			; (Apr - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of April

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Feb, w			; (Feb - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of February

	movf	rtc_daynumL, w		; rtc_daynumL --> w
	subwf	Jan, w			; (Jan - w) --> w
	btfsc	STATUS, Z		; a 'Z' implies a match
	goto	month_match		; It's the last day of January

no_month_match:
	retlw 	D'0'

month_match:
	retlw	D'1'


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

integrate_wwvb:

	btfss	PORTA, 0	; Is WWVB LOW?
	goto	$+6		; Yes.  Branch below

	movlw	D'1'
	subwf	wwvb, f		; WWVB is HIGH.  wwvb--
	btfss	STATUS, C
	decf	wwvb+1, f	; Borrow from wwvb+1

	return
	
	movlw	D'1'
	addwf	wwvb, f		; WWVB is LOW.  wwvb++
	btfsc	STATUS, C
	incf	wwvb+1, f	; Carry into wwvb+1

	return


integrate_bpsk:

	btfss	PORTA, 5	; Is BPSK LOW?
	goto	$+6		; Yes.  Branch below

	movlw	D'1'
	subwf	bpsk, f		; BPSK is HIGH.  bpsk--
	btfss	STATUS, C
	decf	bpsk+1, f	; Borrow from bpsk+1

	return
	
	movlw	D'1'
	addwf	bpsk, f		; BPSK is LOW.  bpsk++
	btfsc	STATUS, C
	incf	bpsk+1, f	; Carry into bpsk+1

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine - Integrate the carrier level received from WWVB over a    ;
; 50ms (maximum) sampling period.  The MSb of "wwvb" holds the result.  ;
;                                                                       ;
; If wwvb+1:7 is HIGH, the bit was HIGH.                                ;
; If wwvb+1:7 is LOW, the bit was LOW.                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Wait50ms:
sample_wwvb:

	clrf	wwvb		; 0 --> wwvb
	clrf	wwvb+1		; 0 --> wwvb+1

	incf	TuningTimer, f
	btfss	TuningTimer, 3
	goto	$+3
	clrf	TuningTimer
	call	DisplayTuning

	btfss	PORTA, 1	; Test logic level of 10 Hz timing pulse
	goto	sample_lo	; Pulse is currently LOW

sample_hi:
	call	integrate_wwvb
	call	integrate_bpsk

	btfsc	PORTA, 1	; Is 10 Hz timing bit still HIGH?
	goto	sample_hi	; Yes.  Keep sampling.

	return			; No.  Exit!

sample_lo:
	call	integrate_wwvb
	call	integrate_bpsk

	btfss	PORTA, 1	; Is 10 Hz timing bit still LOW?
	goto	sample_lo	; Yes.  Keep sampling.

	return			; No.  Exit!


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine - Wait for one full 10 Hz timing cycle to complete. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Wait100ms:

	call	Wait50ms
	call	Wait50ms

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Add 'w' to 16-bit day number ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

add2daynum:

	addwf	wwvb_daynumL, f
	btfsc	STATUS, C
	incf	wwvb_daynumH, f

	return


leapsec_check:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Leap second check.  In this subroutine, we modify         ;;; 
;;; "seconds" if the leap second flag is set, we're at the    ;;;
;;; last day of the current month, and we're at 23:59 UTC.    ;;;
;;; "seconds" is reset back to 59 and rtc_leapsec is cleared  ;;;
;;; in decode_frame() after the leap second has passed.       ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	btfss	rtc_leapsec, 0		; Is leap second flag set?
	return				; No.  Exit.
	call	EndOfMonth		; Is this the end of the month?
	andlw	D'1'			; (w & 1) --> w
	btfsc	STATUS, Z		; Test for zero result
	return				; Not the end of the month.  Exit.
	movf	rtc_hours, w		; rtc_hours --> w
	sublw	D'23'			; (23 - w) --> w
	btfss	STATUS, Z		; Test for zero result		
	return				; rtc_hours != 23.  Exit.
	movf	rtc_minutes, w		; rtc_minutes --> w
	sublw	D'59'			; (59 - w) --> w
	btfss	STATUS, Z		; Test for zero result
	return				; rtc_minutes != 59.  Exit.

	; We're at 23:59 UTC.  Perform leap second count modification

	btfsc	rtc_ut1, 7		; Positive or negative leap?
	goto	$+3			; -UT1 sign --> positive leap
	movlw	D'58'			; +UT1 sign --> negative leap
	goto	$+2			; store in "seconds"
	movlw	D'60'			; 60 --> w
	movwf	seconds			; w --> seconds
	return				; Exit.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This subroutine advances the Real Time Clock by one         ;
; second.  It updates HH:MM:SS, rtc_daynum(H/L) and rtc_year, ;
; and checks and compensates for leap seconds as needed.      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

adv_rtc:

	incf	rtc_seconds, f		; rtc_seconds++
	call	leapsec_check		; Adjust for leap seconds
	movf	rtc_seconds, w		; rtc_seconds --> w
	subwf	seconds, w		; (seconds - w) --> w
	btfsc	STATUS, C		; (rtc_seconds < seconds)?
	return				; Yes.  Exit.

					; No.  (rtc_seconds >= seconds)
	clrf	rtc_seconds		; 0 --> rtc_seconds
	incf	rtc_minutes, f		; rtc_minutes++
	movf	rtc_minutes, w		; rtc_minutes --> w
	sublw	D'60'			; (60 - w) --> w
	btfss	STATUS, Z		; (minutes == 60)?
	return				; No.  Exit.

	clrf	rtc_minutes		; Yes.  0 --> rtc_minutes
	incf	rtc_hours, f		; rtc_hours++
	movf	rtc_hours, w		; rtc_hours --> w
	sublw	D'24'			; (24 - w) --> w
	btfss	STATUS, Z		; (hours == 24)?
	return				; No.  Exit.

	clrf	rtc_hours		; Yes.  0 --> rtc_hours

	;;; If it's December 31st, advance rtc_year and set daynum to 1. ;;;

	btfss	rtc_daynumH, 0		; Test rtc_daynumH
	goto	no_new_year		; It's not Dec 31.  Advance day only.
	movf	rtc_daynumL, w		; Test rtc_daynumL.  rtc_daynumL --> w
	subwf	Dec, w			; (Dec - w) --> w
	btfss	STATUS, Z		; A 'Z' implies a match
	goto	no_new_year		; It's not Dec 31.  Advance day only.
	clrf	rtc_daynumH		; Clear rtc_daynum to zero
	clrf	rtc_daynumL		; because we advance it by 1 below.

	incf	rtc_year, f		; Happy New Year!  (rtc_year++)

no_new_year:

	incf	rtc_daynumL, f		; Advance rtc_daynum by one day
	btfsc	STATUS, Z
	incf	rtc_daynumH, f

	call	Daynum2Cal		; Update the RTC calendar

	return				; We're done.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine - WWVB advances the phase of its carrier +45   ;
; degrees at 10 minutes past each hour, and returns to      ;
; normal phase at 15 minutes past each hour.  This routine  ;
; returns a '1' in 'w' if the RTC is within that zone, or   ;
; a '0' if it is not, and is used to activate the phase     ;
; signature correction hardware in the receiver.            ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; PhaseSigCheck:
;
;	movf	rtc_minutes, w	; rtc_minutes --> w
;	sublw	D'9'		; (9 - w) --> w
;	btfsc	STATUS, C	; Test for positive result
;	retlw	D'0'		; We're < 10 minutes.  0 --> w
;	movf	rtc_minutes, w	; rtc_minutes --> w
;	sublw	D'14'		; (14 - w) --> w
;	btfsc	STATUS, C	; Test for positive result
;	retlw	D'1'		; We're in the "zone".  1 --> w
;	retlw	D'0'		; We're >= 15 minutes.  0 --> w

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine - Set real-time clock.  Since we set the clock ;
; at second 59 in the time code, rtc_seconds is set to 59.  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

set_rtc:
	movlw	D'59'		; 59 --> w
	movwf	rtc_seconds	; w --> rtc_seconds
	movwf	countdown
	incf	countdown, f	; set "countdown" timer to T-60
	movf	wwvb_minutes, w
	movwf	rtc_minutes	; wwvb_minutes --> rtc_minutes
	movf	wwvb_hours, w
	movwf	rtc_hours	; wwvb_hours --> rtc_hours
	movf	wwvb_year, w
	movwf	rtc_year	; wwvb_year --> rtc_year
	movf	wwvb_ut1, w
	movwf	rtc_ut1		; wwvb_ut1 --> rtc_ut1

	movf	wwvb_daynumL, w	; wwvb_daynumL --> w
	movwf	rtc_daynumL	; lower 8-bits of daynum --> rtc_daynumL

	movf	wwvb_daynumH, w	; wwvb_daynumH --> w
	movwf	rtc_daynumH	; upper 8-bits of daynum --> rtc_daynumH

	movf	wwvb_leapsec, w	; wwvb_leapsec --> w
	movwf	rtc_leapsec	; w --> rtc_leapsec

	bsf	rtc_status, 0	; set "clock set" bit

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Verify the RTC against the current frame. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

verify_rtc:

	movf	wwvb_minutes, w	; wwvb_minutes --> w
	subwf	rtc_minutes, w	; (rtc_minutes - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return			; exit if there's no match

	movf	wwvb_hours, w	; wwvb_hours --> w
	subwf	rtc_hours, w	; (rtc_hours - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return			; exit if there's no match

	movf	wwvb_year, w	; wwvb_year --> w
	subwf	rtc_year, w	; (rtc_year - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return			; exit if there's no match

	movf	wwvb_ut1, w	; wwvb_ut1 --> w
	subwf	rtc_ut1, w	; (rtc_ut1 - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return			; exit if there's no match

	movf	wwvb_daynumH, w	; upper 8-bits of wwvb_daynum --> w
	subwf	rtc_daynumH, w	; (rtc_daynumH - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return

	movf	wwvb_daynumL, w	; lower 8-bits of wwvb_daynum --> w
	subwf	rtc_daynumL, w	; (rtc_daynumL - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return

	movf	wwvb_leapsec, w	; wwvb_leapsec --> w
	subwf	rtc_leapsec, w	; (rtc_leapsec - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero difference
	return			; exit of there's no match

	call	Daynum2Cal	; Set calendar (rtc_month, rtc_day)

	;;; Check to see if we're starting inside WWVB's phase
	;;; signature period.  Set the "initial phase shift flag"
	;;; in rtc_status (bit 4) accordingly.

;	call	PhaseSigCheck	; Are we within the phase shift period?
;	andlw	D'1'		; Set the STATUS flag based on the result.
;	btfsc	STATUS, Z	; A 'Z' implies we're OUTSIDE the period.
;	goto	$+3		; We're outside.  Go to bcf
;	bsf	rtc_status, 4	; We're inside. --> Set the phase shift bit.
;	goto	$+2		; Jump over the next statement.	
;	bcf	rtc_status, 4	; We're outside. --> Clear the bit

	bsf	rtc_status, 1	; Set "clock verified" bit

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Convert a 16-bit binary number     ;
; in NumH:NumL to BCD in TenK:Thou:Hund:Tens:Ones. ;
; Written by John Payson                           ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Bin2BCD:
	swapf	NumH, w
	iorlw	D'240'
	movwf	Thou
	addwf	Thou, f
	addlw	D'226'
	movwf	Hund
	addlw	D'50'
	movwf	Ones

	movf	NumH, w
	andlw	D'15'
	addwf	Hund, f
	addwf	Hund, f
	addwf	Ones, f
	addlw	D'233'
	movwf	Tens
	addwf	Tens, f
	addwf	Tens, f

	swapf	NumL, w
	andlw	D'15'
	addwf	Tens, f
	addwf	Ones, f

	rlf	Tens, f
	rlf	Ones, f
	comf	Ones, f
	rlf	Ones, f

	movf	NumL, w
	andlw	D'15'
	addwf	Ones, f
	rlf	Thou, f

	movlw	D'7'
	movwf	TenK

	movlw	D'10'

Lb1:
	addwf	Ones, f
	decf	Tens, f
	btfss	STATUS, C
	goto 	Lb1

Lb2:
	addwf	Tens, f
	decf	Hund, f
	btfss 	STATUS, C
	goto 	Lb2

Lb3:
	addwf	Hund, f
	decf	Thou, f
	btfss	STATUS, C
	goto	Lb3

Lb4:
	addwf	Thou, f
	decf	TenK, f
	btfss	STATUS, C
	goto 	Lb4

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- 16-bit subtraction as follows: ;
;     R_HI:R_LO = Q1_HI:Q1_LO - Q2_HI:Q2_LO    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

Sub16:
	movf	Q2_LO, w
	subwf	Q1_LO, w
	movwf	R_LO

	btfss	STATUS, C
	goto	$+2
	goto	$+2
	decf	Q1_HI, f	; borrow from Q1 high-order byte
	movf	Q2_HI, w
	subwf	Q1_HI, w
	movwf	R_HI

	return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Get into sync with the start of the PWM pulse.  Wait    ;
; for a PWM falling edge that coincides with the 10 Hz timing pulse,    ;
; then use correlation decoding to determine when proper framing has    ;
; occurred.  Correlate only the first and last 200 ms of each PWM pulse ;
; since these are the same regardless of the pulse type.  Exit out of   ;
; this routine only after errorless framing is repeated 8 times in a    ;
; row.                                                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

pwm_sync:
	clrf	counter		; clear success counter

	;;; Wait until a PWM falling edge is detected.

	call	Wait50ms
	btfss	PORTA, 0	; Loop while WWVB carrier is LOW
	goto	$-2

	btfsc	PORTA, 0	; Loop while WWVB carrier is HIGH
	goto	$-1

	;;; At this point, we are at the falling edge (beginning)
	;;; of the PWM pulse.

	;;; Send a short RESET pulse to the 74HC390 counter delivering
	;;; the 10 Hz timing pulse to put it in sync with the falling
	;;; edge of the time code pulse.

	bsf	PORTA, 4	; Set the 74HC390 reset pulse HIGH
	clrf	tmpctr

	incf	tmpctr, f	; Perform a short timing loop to
	btfss	tmpctr, 4	; keep the '390 reset pulse high
	goto	$-2

	bcf	PORTA, 4	; Return the 74HC390 reset pulse to LOW
	
pwm_start:

	clrf	signal		; 0--> signal (accumulator)

	;;; Test the first four 50ms periods of the PWM pulse.  The pulse
	;;; should be LOW for the first four periods (200 ms) of the second.

	clrf	tmpctr		; (temp counter)

	call	sample_wwvb	; Sample WWVB for 50ms (max)

	btfss	wwvb+1, 7	; if bit is low,
	incf	signal, f	; 	signal++
	btfsc	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	incf	tmpctr, f	; tmpctr++
	btfss	tmpctr, 2 	; Until we reach 4,
	goto	$-7		; check next sample

	call	Wait100ms	; 200 ms --> 300 ms \
	call	Wait100ms	; 300 ms --> 400 ms  \
	call	Wait100ms	; 400 ms --> 500 ms   Ignore the middle 600 ms
	call	Wait100ms	; 500 ms --> 600 ms   of the time code pulse
	call	Wait100ms	; 600 ms --> 700 ms  /
	call	Wait100ms	; 700 ms --> 800 ms /

	;;; Test the last four 50ms periods of the PWM pulse.  The pulse
	;;; should be HIGH for the last four periods (200 ms) of the second.

	clrf	tmpctr		; (temp counter)

	call	sample_wwvb	; Sample WWVB for 50ms (max)

	btfsc	wwvb+1, 7	; if bit is high,
	incf	signal, f	; 	signal++
	btfss	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	incf	tmpctr, f	; tmpctr++
	btfss	tmpctr, 2 	; Until we reach 4,
	goto	$-7		; check next sample

	;;; Evaluate the results.

	movf	signal, w	; signal --> w
	sublw	D'8'		; (8 - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a perfect correlation
	goto	pwm_sync	; No good?  --> Try again for sync

	incf	counter, f	; We have sync.  Bump match counter.
	btfss	counter, 3	; Have we found 8 consecutive matches? 
	goto	pwm_start	; No! --> Test next PWM pulse.
	return			; Yes!  -->  We're outta here.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Identify the PWM pulse type and meaning.                ;
;                                                                       ;
; First initialize three accumulators, one for each PWM pulse type.     ;
; Initialization value is relatively unimportant provided overflows     ;
; or underflows don't occur during the integration period.              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

decode_pulse:

	movlw	D'60'		; Initialize accumulators to a convenient number
	movwf	zero		; Accumulator for the "zero" bit
	movwf	one		; Accumulator for the "one" bit
	movwf	marker		; Accumulator for the "marker" bit
	clrf	counter		; clear counter

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; Sample WWVB every 50 ms, integrate across three correlators,   ;
	; and compare the results.  The highest correlation wins.  :-)   ;
	; We only need to examine the center 600 ms of each pulse to     ;
	; identify its type (zero, one, marker).  The surrounding        ;
	; 400 ms are identical for each pulse type, so those periods are ;
	; correlated separately to determine the "quality" of signal     ;
	; received.                                                      ;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;                                                                ;
	; zero:		200 ms low, 800 ms high                          ;
	; one:		500 ms low, 500 ms high                          ;
	; marker:	800 ms low, 200 ms high                          ;
	;                                                                ;
	; The time code is read from PORT A, bit 0 (pin 17).             ;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	;;; Test "bit quality": 0 ms --> 200 ms  (should be LOW)

	clrf	tmpctr		; 0 --> tmpctr
	clrf	signal		; 0 --> signal

	call	sample_wwvb	; Sample WWVB

	btfss	wwvb+1, 7	; if bit is low,
	incf	signal, f	; 	signal++
	btfsc	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	incf	tmpctr, f	; tmpctr++
	btfss	tmpctr, 2 	; Until we reach 4, (4 x 50ms = 200 ms)
	goto	$-7		; check next sample

first:

	;;; 200 ms --> 500 ms : 'zero' is HIGH, 'one' and 'marker' are LOW

	call	sample_wwvb	; Sample WWVB

	btfss	wwvb+1, 7	; Test PWM pulse
	goto	$+5
	incf	zero, f		; zero++	\
	decf	one, f		; one--		 Bit is HIGH
	decf	marker, f	; marker--	/
	goto	$+4

	decf	zero, f		; zero--	\
	incf	one, f		; one++		 Bit is LOW
	incf	marker, f	; marker++	/

	incf	counter, f	; counter++
	movf	counter, w	; counter --> w
	sublw	D'6'		; (6 - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero result (w == 6)
	goto	first		; Loop around 6 times

	clrf	counter

second:

	;;; 500ms --> 800ms : 'zero' is HIGH, 'one' is HIGH, 'marker' is LOW

	call	sample_wwvb	; Sample WWVB (750ms --> 800ms)

	btfss	wwvb+1, 7	; Test PWM pulse
	goto	$+5
	incf	zero, f		; zero++	\
	incf	one, f		; one++		 Bit is HIGH
	decf	marker, f	; marker--	/
	goto	$+4

	decf	zero, f		; zero--	\
	decf	one, f		; one--		 Bit is LOW
	incf	marker, f	; marker++	/

	incf	counter, f	; counter++
	movf	counter, w	; counter --> w
	sublw	D'6'		; (6 - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a zero result (w == 6)
	goto	second		; Loop around 6 times

	movlw	D'196'		; Move LCD cursor to the BPSK position
	call	LCD_CMD
	movlw	' '
	call	LCD_Byte	; Blink the BPSK Character OFF

	movlw	D'197'		; Move LCD cursor to PWM character position
	call	LCD_CMD
	movlw	' '
	call	LCD_Byte	; Blink The PWM Character OFF

	;;; Test "bit quality" : 800ms --> 850ms (should be HIGH)

	call	sample_wwvb	; Sample WWVB

	btfsc	wwvb+1, 7	; if bit is high,
	incf	signal, f	; 	signal++
	btfss	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	btfsc	rtc_status, 0	; If RTC has been set,
	call	adv_rtc		; advance RTC one second

	;;; Test "bit quality" : 850ms --> 900ms (should be HIGH)

	call	sample_wwvb	; Sample WWVB

	btfsc	wwvb+1, 7	; if bit is high,
	incf	signal, f	; 	signal++
	btfss	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	call	DisplayTime	; Display the time (or status message)

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; Compare the results from each accumulator  ;
	; and tabulate a final result.               ;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	clrf	zero_one
	clrf	one_marker
	clrf	zero_marker

	movf	one, w		; one --> w
	subwf	zero, w		; (zero-one) --> w
	btfsc	STATUS, C 	; Positive result?
	comf	zero_one, f	; Yes! 255 --> zero_one

	movf	marker, w	; marker --> w
	subwf	one, w		; (one-marker) --> w
	btfsc	STATUS, C	; Positive result?
	comf	one_marker, f	; Yes! 255 --> one_marker

	movf	marker, w	; marker --> w
	subwf	zero, w		; (zero-marker) --> w
	btfsc	STATUS, C	; Positive result?
	comf	zero_marker, f	; Yes! 255 --> zero_marker

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; Perform logic on the results as follows:             ;
	;                                                      ;
	; A zero result	= (zero > one) & (zero > marker)       ;
	; A one result = (one > marker) & !(zero > one)        ;
	; A marker result = (zero > marker) NOR (one > marker) ;
	;                                                      ;
	; Results are assigned as follows:                     ;
	;                                                      ;
	;           '0' --> "pwm" bit 0 is set                 ;
	;           '1' --> "pwm" bit 1 is set                 ;
	;      'marker' --> "pwm" bit 2 is set                 ;
	; 'poor signal' --> "pwm" bit 7 is set                 ;	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	clrf	pwm

	;;; PWM = "zero" ;;;

	movf	zero_one, w	; zero_one --> w
	andwf	zero_marker, w	; (zero_one & zero_marker) --> w
	andlw	D'1'		; (w AND 1) --> w (set bit 0)
	iorwf	pwm, f		; store result in pwm bit 0

	;;; PWM = "one" ;;;

	comf	zero_one, w	; invert zero_one and store in w
	andwf	one_marker, w	; (inverted zero_one & one_marker) --> w
	andlw	D'2'		; (w AND 2) --> w (set bit 1)
	iorwf	pwm, f		; store result in pwm bit 1

	;;; PWM = "marker" ;;;

	movf	one_marker, w	; one_marker --> w
	iorwf	zero_marker, w	; (one_marker | zero_marker) --> w
	xorlw	D'255'		; invert result to produce a NOR function
	andlw	D'4'		; (w AND 4) --> w (set bit 2)
	iorwf	pwm, f		; store result in pwm bit 2

	;;; Test "bit quality" : 900 --> 1000 ms (should be HIGH)


	clrf	tmpctr

	call	sample_wwvb	; Sample WWVB

	btfsc	wwvb+1, 7	; if bit is high,
	incf	signal, f	; 	signal++
	btfss	wwvb+1, 7	; else
	decf	signal, f	; 	signal--

	incf	tmpctr, f	; tmpctr++
	btfss	tmpctr, 1 	; Until we reach 2, (100 ms)
	goto	$-7		; check next sample

	btfsc	signal, 7	; Is (signal < 0)?
	goto	$+3		; Yes!  --> badly corrupted bit
	movf	signal, f	; signal --> signal (to set Z flag)
	btfsc	STATUS, Z	; Is (signal == 0)?
	bsf	pwm, 7		; Yes!  Poor (or non-existent) signal.	

	;;; Display BPSK Bit on the LCD display ('+' OR '-')

	movlw	D'196'		; Move cursor into BPSK position
	call	LCD_CMD

	btfsc	bpsk+1, 7	; Test MSb of bpsk+1
	goto	bpn

	movlw	'+'
	goto	$+2

bpn:	movlw	'-'

	call	LCD_Byte	; Display BPSK Character on the LCD

	clrf	bpsk
	clrf	bpsk+1

	;;; Display PWM pulse type on the LCD display ('0', '1', 'M').
	;;; If no signal is present, display a '?' character.

	movlw	D'197'		; Move cursor into PWM display position
	call	LCD_CMD

	btfsc	pwm, 0		; Assign the appropriate PWM pulse character
	movlw	'0'
	btfsc	pwm, 1
	movlw	'1'
	btfsc	pwm, 2
	movlw	'M'
	btfsc	pwm, 7
	movlw	'?'

	call	LCD_Byte	; Display the PWM pulse character

	movlw	D'199'		; Move to the "bit quality" character position
	call	LCD_CMD
	movf	signal, w	; signal --> w
	btfsc	signal, 7	; Is "signal" negative?
	clrw			; Yes!  Display it as a '0'.
	call	LCD_Digit	; Display "bit quality" character (0 -> 8)

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Display TCVCXO tuning voltage on the LCD Display.   ;
; One half the tuning voltage is applied to AN2 (A/D channel 2) on  ;
; pin 1, and one half the 6v reference voltage is applied to AN3 on ;
; pin 2.  Resolution is 6V/1024 = 5.8mV                             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DisplayTuning:

	;;; Perform the A/D conversion ;;;

	bsf	ADCON0, 2	; Set bit 2 to start A/D

	btfsc	ADCON0, 2	; Loop until bit 2 goes clear
	goto	$-1		; indicating the A/D conversion is complete

	;;; Grab results ;;;

	movf	ADRESH, w	; ADRESH --> w
	movwf	NumH		; w --> NumH
	bsf	STATUS, RP0	; Switch to Bank 1 to get ADRESL
	movf	ADRESL^0x080, w	; ADRESL --> w
	bcf	STATUS, RP0	; Switch back to Bank 0
	movwf	NumL		; w --> NumL

	;;; Move LCD Cursor into position

	movlw	D'201'
	call	LCD_CMD

	;;; Center results on 512 (center of A/D range)

	btfsc	NumH, 1		; If bit 1 of NumH is set,
	goto	PositiveResult	; we have a '+' or ' ' result.

	;;; Negative Result

	movlw	'-'		; Negative sign
	call	LCD_Byte	; Display the negative sign

	;;; Result = (512 - NumH:NumL)

	movlw	D'2'		; Q1 = 512
	movwf	Q1_HI
	clrf	Q1_LO

	movf	NumH, w		; Q2 = NumH:NumL
	movwf	Q2_HI
	movf	NumL, w
	movwf	Q2_LO

	call	Sub16

	movf	R_LO, w
	movwf	NumL
	movf	R_HI, w
	movwf	NumH

	goto	DisplayResults

PositiveResult:

	;;; Test for NumH:NumL = 512 (0x0200)

	movf	NumH, w		; NumH --> w 
	sublw	D'02'		; (2 - w) --> w
	btfss	STATUS, Z	; a 'Z' implies a ZERO result (NumH = 2)
	goto	plus_sign	; Result is not 512.  Skip the following.
	movf	NumL, f		; Test low byte of A/D result
	btfsc	STATUS, Z	; Zero result --> Z gets set
	goto	no_sign

plus_sign:
	movlw	'+'
	goto	$+2

no_sign:
	movlw	' '
	call	LCD_Byte

	;;; Result = (NumH:NumL - 512)

	movf	NumH, w		; Q1 = NumH:NumL
	movwf	Q1_HI
	movf	NumL, w
	movwf	Q1_LO

	movlw	D'2'		; Q2 = 512
	movwf	Q2_HI
	clrf	Q2_LO

	call	Sub16

	movf	R_LO, w
	movwf	NumL
	movf	R_HI, w
	movwf	NumH

	;;; Display results on LCD display ;;;

DisplayResults:

	call	Bin2BCD		; Convert binary A/D result to BCD
	movf	Hund, w		; Hund --> w
	call	LCD_Digit	; Display digit
	movf	Tens, w		; Tens --> w
	call	LCD_Digit	; Display digit
	movf	Ones, w		; Ones --> w
	call	LCD_Digit	; Display digit

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Get in sync with the beginning of the time code     ;
; frame.  The beginning of the frame is identified by the reception ;
; of two consecutive position identification markers.  Find 'em!    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

frame_sync:

	bcf	rtc_status, 2		; Clear "framing" bit	
	clrf	marker_count		; 0 --> marker_count
fs2:	call	decode_pulse		; Decode the time code bit
	btfsc	pwm, 7			; Bit 7 is HIGH if the bit is bad
	goto	frame_sync		; Poor (or no) signal.  Start over.
	btfss	pwm, 2			; Is it a position marker?
	goto	frame_sync		; No?  Keep looking

	incf	marker_count, f		; Yes?  marker_count++
	btfss	marker_count, 1		; Did the count reach two?
	goto	fs2			; No?  Check the next pulse
	bsf	rtc_status, 2		; Yes?  Set "framing" bit
	movlw	D'58'			; Set countdown timer to 58
	movwf	countdown
	return				; And exit the routine

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Decode the time code, set and verify the RTC as  ;
; needed.  Note: This routine must be entered immediately AFTER  ;
; the Frame Reference Bit (Pr) has ended.                        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

decode_frame:

	;;; Frame Reference Bit, Pr, has just ended ;;;;;;;;;;;;;;;;;

	bcf	rtc_status, 3	; Clear "minor success" bit

	call	decode_pulse	; Minutes, 40	;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_minutes	; 0 ---> wwvb_minutes		
	movlw	D'40'		; 40 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	movwf	wwvb_minutes	; 	w --> wwvb_minutes

	call	decode_pulse	; Minutes, 20	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'20'		; 20 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_minutes, f	; 	wwvb_minutes = wwvb_minutes + 20

	call	decode_pulse	; Minutes, 10	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'10'		; 10 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_minutes, f	; 	wwvb_minutes = wwvb_minutes + 10

	call	decode_pulse	; Reserved

	call	decode_pulse	; Minutes, 8	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'8'		; 8 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_minutes, f	; 	wwvb_minutes = wwvb_minutes + 8

	call	decode_pulse	; Minutes, 4	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'4'		; 4 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_minutes, f	; 	wwvb_minutes = wwvb_minutes + 4

	call	decode_pulse	; Minutes, 2	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'2'		; 2 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_minutes, f	; 	wwvb_minutes = wwvb_minutes + 2

	call	decode_pulse	; Minutes, 1	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	btfsc	pwm, 1		; if (pwm == 1)
	incf	wwvb_minutes, f	; 	wwvb_minutes++

	call	decode_pulse	; Marker, P1

	call	decode_pulse	; Reserved
	call	decode_pulse	; Reserved

	call	decode_pulse	; Hours, 20	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_hours	; 0 --> wwvb_hours
	movlw	D'20'		; 20 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	movwf	wwvb_hours	; 	w --> wwvb_hours

	call	decode_pulse	; Hours, 10	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'10'		; 10 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_hours, f	; 	wwvb_hours = wwvb_hours + 10

	call	decode_pulse	; Reserved

	call	decode_pulse	; Hours, 8	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC 

	movlw	D'8'		; 8 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_hours, f	; 	wwvb_hours = wwvb_hours + 8

	call	decode_pulse	; Hours, 4	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'4'		; 4 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_hours, f	; 	wwvb_hours = wwvb_hours + 4

	call	decode_pulse	; Hours, 2	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'2'		; 2 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_hours, f	; 	wwvb_hours = wwvb_hours + 2

	call	decode_pulse	; Hours, 1	;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	btfsc	pwm, 1		; if (pwm == 1)
	incf	wwvb_hours, f	; 	wwvb_hours++

	call	decode_pulse	; Marker, P2

	call	decode_pulse	; Reserved
	call	decode_pulse	; Reserved

	call	decode_pulse	; Day of year, 200	;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_daynumL
	clrf	wwvb_daynumH

	movlw	D'200'		; 200 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 200

	call	decode_pulse	; Day of year, 100	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'100'		; 100 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 100

	call	decode_pulse	; Reserved

	call	decode_pulse	; Day of year, 80	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'80'		; 80 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 80

	call	decode_pulse	; Day of year, 40	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'40'		; 40 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 40

	call	decode_pulse	; Day of year, 20	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'20'		; 20 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 20

	call	decode_pulse	; Day of year, 10	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'10'		; 10 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 10

	call	decode_pulse	; Marker, P3

	call	decode_pulse	; Day of year, 8	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'8'		; 8 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 8

	call	decode_pulse	; Day of year, 4	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'4'		; 4 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 4

	call	decode_pulse	; Day of year, 2	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'2'		; 2 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 2

	call	decode_pulse	; Day of year, 1	;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'1'		; 1 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	call	add2daynum	; 	daynum = daynum + 1

	call	decode_pulse	; Reserved
	call	decode_pulse	; Reserved

	call	decode_pulse	; UT1 Sign, +		;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_ut1
	btfsc	pwm, 1		; if (pwm == 1)
	bcf	wwvb_ut1, 7	; 	clear wwvb_ut1 bit 7

	call	decode_pulse	; UT1 Sign, -		;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	btfsc	pwm, 1		; if (pwm == 1)
	bsf	wwvb_ut1, 7	; 	set wwvb_ut1 bit 7

	call	decode_pulse	; UT1 Sign, +		;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	btfsc	pwm, 1		; if (pwm == 1)
	bcf	wwvb_ut1, 7	; 	clear wwvb_ut1 bit 7

	call	decode_pulse	; Marker, P4

	call	decode_pulse	; UT1 correction, 0.8 seconds	;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'8'		; 8 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_ut1, f	; 	wwvb_ut1 = wwvb_ut1 + 8

	call	decode_pulse	; UT1 correction, 0.4 seconds	;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'4'		; 4 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_ut1, f	; 	wwvb_ut1 = wwvb_ut1 + 4

	call	decode_pulse	; UT1 correction, 0.2 seconds	;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'2'		; 2 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_ut1, f	; 	wwvb_ut1 = wwvb_ut1 + 2

	call	decode_pulse	; UT1 correction, 0.1 seconds	;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'1'		; 1 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_ut1, f	; 	wwvb_ut1 = wwvb_ut1 + 1

	call	decode_pulse	; Reserved

	call	decode_pulse	; Year, 80	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_year	; 0 --> wwvb_year
	movlw	D'80'		; 80 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	movwf	wwvb_year	; 	w --> wwvb_year

	call	decode_pulse	; Year, 40	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'40'		; 40 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 40

	call	decode_pulse	; Year, 20	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'20'		; 20 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 20

	call	decode_pulse	; Year, 10	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'10'		; 10 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 10

	call	decode_pulse	; Marker P5

	call	decode_pulse	; Year, 8	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'8'		; 8 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 8

	call	decode_pulse	; Year, 4	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'4'		; 4 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 4

	call	decode_pulse	; Year, 2	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	movlw	D'2'		; 2 --> w
	btfsc	pwm, 1		; if (pwm == 1)
	addwf	wwvb_year, f	; 	wwvb_year = wwvb_year + 2

	call	decode_pulse	; Year, 1	;;;;;;;;;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	btfsc	pwm, 1		; if (pwm == 1)
	incf	wwvb_year, f	; 	wwvb_year ++

	call	decode_pulse	; Reserved

	call	decode_pulse	; Leap Year Indicator

	call	decode_pulse	; Leap Second Warning   ;;;;;;;;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	$+3		; Yes!  Skip the following sanity check.

	btfsc	pwm, 2		; if (pwm == marker)
	return			; 	exit without setting RTC

	clrf	wwvb_leapsec	; 0 --> wwvb_leapsec

	btfsc	pwm, 1		; if (pwm == 1)
	comf	wwvb_leapsec, f	; 	255 --> wwvb_leapsec

	call	decode_pulse	; Daylight savings time flag
	call	decode_pulse	; Daylight savings time flag

	;;;; Check RTC status ;;;;;;;;;

	btfsc	rtc_status, 1	; Has the RTC been verified?
	goto	done		; Yes? --> We're outta here.

	btfsc	rtc_status, 0	; Has the RTC been set?
	goto	ver		; Yes? --> Verify it
	call	set_rtc		; No?  Set RTC to decoded time
	goto	done		; We're outta here

	;;;; Verify the RTC ;;;;;;;;;;;;

ver:	call	verify_rtc	; Verify the RTC

	btfsc	rtc_status, 1	; Was it successful?
	goto	done		; Yes!  Continue decoding at "done".

	bcf	rtc_status, 0	; No! -->  Clear "set clock" bit
	return			; Bail out

done:	call	decode_pulse	; Frame Reference Bit, P0

	;;;;; End of frame -- beginning of new minute ;;;;;

	;;; Do we have a leap second with which to contend? ;;;

	movf	seconds, w	; seconds --> w
	sublw	D'59'		; (59 - w) --> w
	btfsc	STATUS, Z	; (seconds == 59)?  (Normal value)
	goto	noleap		; Yes.  No leap second processing.

	;; We have a leap second

	sublw	D'60'		; (60 - w) --> w	
	btfsc	STATUS, Z	; (seconds == 60)?  (Positive leap second)
	goto	posleap		; Yes!  Continue at posleap

	;; Negative leap second

	bsf	rtc_status, 3	; Set "minor success" bit
	call	ut1_adjust	; Modify the UT1 offset
	movlw	D'59'		; 59 --> w
	movwf	seconds		; Reset "seconds" variable back to 59
	clrf	rtc_leapsec	; 0 --> rtc_leapsec
	return			; Bypass reading the Pr bit below

posleap:
	;;; Positive leap second

	call	decode_pulse	; Read the leap second (Marker bit)
	call	ut1_adjust	; Modify the UT1 offset
	movlw	D'59'		; 59 --> w
	movwf	seconds		; Reset "seconds" variable back to 59
	clrf	rtc_leapsec	; 0 --> rtc_leapsec

noleap:
	call	decode_pulse	; Frame Reference Bit, Pr

	bsf	rtc_status, 3	; Set "minor success" bit

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- This code modifies the UT1 offset after a    ;
; leap second has passed.  It does this by setting rtc_ut1   ;
; to 10-abs(rtc_ut1) and reversing rtc_ut1's sign from what  ;
; it was originally.  This routine compensates for the added ;
; (or skipped) leap second.                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

ut1_adjust:

	btfss	rtc_ut1, 7	; Is UT1 sign bit high?  (Negative value?)
	goto	$+6		; No.  Positive number.  Process it below.
	bcf	rtc_ut1, 7	; Yes.  Negative value.  Make it positive.
	movf	rtc_ut1, w	; rtc_ut1 --> w
	sublw	D'10'		; (10 - rtc_ut1) --> w
	movwf	rtc_ut1		; w --> rtc_ut1
	return			; We're done

				; Process a positive UT1 value
	movf	rtc_ut1, w	; rtc_ut1 --> w
	sublw	D'10'		; (10 - rtc_ut1) --> w
	movwf	rtc_ut1		; w --> rtc_ut1
	bsf	rtc_ut1, 7	; Set sign bit to negative
	return			; We're done.


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Send 4-bit nibble plus R/S bit to LCD    ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This routine sends LCD R/S (Register Select) bit plus  ;
; 4-bits of data to the LCD display connected to PORT B. ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

NibbleOUT:
	movwf	ctr		; w --> ctr (temp variable)

	btfsc	ctr, 0		; Map RB0 (pin 6) --> LCD's DB4
	bsf	PORTB, 0
	btfss	ctr, 0
	bcf	PORTB, 0

	btfsc	ctr, 1		; Map RB1 (pin 7) --> LCD's DB5
	bsf	PORTB, 1
	btfss	ctr, 1
	bcf	PORTB, 1

	btfsc	ctr, 2		; Map RB7 (pin 13) --> LCD's DB6
	bsf	PORTB, 7
	btfss	ctr, 2
	bcf	PORTB, 7

	btfsc	ctr, 3		; Map RB3 (pin 9) --> LCD's DB7
	bsf	PORTB, 3
	btfss	ctr, 3
	bcf	PORTB, 3

	btfsc	ctr, 4		; Map RB4 (pin 10) --> LCD's R/S line
	bsf	PORTB, 4
	btfss	ctr, 4
	bcf	PORTB, 4

	bsf	PORTB, 6	; toggle 'E' bit
	bcf	PORTB, 6	; toggle 'E' bit

	clrf	ctr		; execute a small delay
	nop			; before returning
	nop
	incf	ctr, f
	btfss	ctr, 6
	goto	$-4		; timing loop (first "nop")

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Send the contents of w as an instruction  ;
; to the LCD display.                                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LCD_CMD:
	movwf	temp		; w --> temp
	swapf	temp, w		; (swap temp) --> w
	andlw	D'15'		; (w AND 15) --> w
	call	NibbleOUT	; Send the high nibble
	movf	temp, w		; temp --> w
	andlw	D'15'		; (w AND 15) --> w
	call	NibbleOUT	; Send the low nibble
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Send the contents of w as a register      ;
; select byte to the LCD display.                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LCD_Byte:
	movwf	temp		; w --> temp
	swapf	temp, w		; (swap temp) --> w
	andlw	D'15'		; (w AND 15) --> w
	iorlw	D'16'		; R/S --> 1 (Register select)
	call	NibbleOUT	; Send the high nibble
	movf	temp, w		; temp --> w
	andlw	D'15'		; (w AND 15) --> w
	iorlw	D'16'		; R/S --> 1 (Register select)
	call	NibbleOUT	; Send the low nibble
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Subroutine -- Send a numeric digit in 'w' to the LCD display  ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LCD_Digit:
	iorlw	D'48'
	call	LCD_Byte
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Initialize the LCD display.  Display is   ;
; connected to PORT B.  Bits 0 to 3 --> D4 to D7 on LCD   ;
; Bit 4 = R/S line.  Bit 6 = 'E' bit.  R/W is tied to GND ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LCD_Init:
	call	Wait50ms	; Wait before reset
	bcf	PORTB, 4	; 0 --> R/S bit (Instruction command)
	movlw	D'03'		; 3 --> w
	call	NibbleOUT	; reset command
	call	Wait50ms	; Wait...
	movlw	D'03'		; 3 --> w
	call	NibbleOUT	; reset command
	call	Wait50ms	; Wait...
	movlw	D'03'		; 3 --> w
	call	NibbleOUT	; reset command
	call	Wait50ms	; Wait...
	
	; Now set the operating characteristics of the LCD

	bcf	PORTB, 4	; 0 --> R/S bit (Instruction)
	movlw	D'02'		; 2 --> w (set 4-bit mode)
	call 	NibbleOUT	; send it off
	call	Wait50ms	; Wait...
	movlw	D'40'		; 4-bit, 2-line, 5x7 font mode
	call	LCD_CMD		; send it off
	movlw	D'16'		; Display shift off
	call	LCD_CMD		; send it off
	movlw	D'1'		; Clear the display RAM
	call	LCD_CMD		; send it off
	call	Wait50ms	; Wait...
	movlw	D'6'		; Increment cursor
	call	LCD_CMD		; send it off
	call	Wait50ms	; Wait..
	movlw	D'12'		; display cursor off
	call	LCD_CMD		; send it off
	call	Wait50ms	; wait...
	return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Subroutine -- Display progress countdown timer ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DisplayCountdown:

	movlw	D'150'		; Display "countdown" timer on
	call	LCD_CMD		; LCD display while timecode is
	clrf	NumH		; being received and verified.
	movf	countdown, w	; countdown --> w
	movwf	NumL		; w --> NumL
	call	Bin2BCD
	movf	Tens, w
	call	LCD_Digit
	movf	Ones, w
	call	LCD_Digit
	decf	countdown, f	; countdown --

	return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine: Displays Day of Week on the LCD Display ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DisplayDayOfWeek:

	movlw	D'140'		; Set LCD cursor into position
	call	LCD_CMD

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'6'		; (6 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'S'		; It's Saturday
	call	LCD_Byte
	movlw	'a'
	call	LCD_Byte
	movlw	't'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'5'		; (5 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'F'		; It's Friday
	call	LCD_Byte
	movlw	'r'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'4'		; (4 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'T'		; It's Thursday
	call	LCD_Byte
	movlw	'h'
	call	LCD_Byte
	movlw	'u'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'3'		; (3 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'W'		; It's Wednesday
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	'd'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'2'		; (2 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'T'		; It's Tuesday
	call	LCD_Byte
	movlw	'u'
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	sublw	D'1'		; (1 - w) --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match
	movlw	'M'		; It's Monday
	call	LCD_Byte
	movlw	'o'
	call	LCD_Byte
	movlw	'n'
	call	LCD_Byte
	return

	movf	rtc_dayofweek,w	; rtc_dayofweek --> w
	btfss	STATUS, Z	; Zero result?
	goto	$+8		; Not a match   Yikes!  :-O
	movlw	'S'		; It's Sunday
	call	LCD_Byte
	movlw	'u'
	call	LCD_Byte
	movlw	'n'
	call	LCD_Byte
	return

	movlw	'?'		; We should never get here...
	call	LCD_Byte
	movlw	'?'
	call	LCD_Byte
	movlw	'?'
	call	LCD_Byte
	return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Subroutine -- Display the time or progress messages ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DisplayTime:

	;;; Take care of progress messages first ;;;

	btfsc	rtc_status, 2	; Is "framing" bit high?
	goto	$+3		; Yes
	call	msg2		; No --> "Waiting For Frame Sync"
	return

	btfsc	rtc_status, 0	; Is "clock set" bit high?
	goto	$+4		; Yes
	call	msg3		; No --> "Receiving Date/Time"
	call	DisplayCountdown
	return

	btfsc	rtc_status, 1	; Is "verify" bit high?
	goto	$+4		; Yes
	call	msg4		; No --> "Verifying Date/Time"
	call	DisplayCountdown
	return

	;;; If we made it this far, we can display the time/date ;;;

	btfss	rtc_status, 7	; LCD "setup" bit
	call	msg5		; Time / Date LCD "set up"

	movlw	D'128'		; Cursor home
	call	LCD_CMD

	movf	rtc_hours, w	; rtc_hours --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	HH
	call	LCD_Digit
	movf	Ones, w
	movwf	hh
	call	LCD_Digit
	movlw	':'
	call	LCD_Byte	; Display HH:

	movf	rtc_minutes, w	; rtc_minutes --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	MM
	call	LCD_Digit
	movf	Ones, w
	movwf	mm
	call	LCD_Digit
	movlw	':'
	call	LCD_Byte	; Display MM:

	movf	rtc_seconds, w	; rtc_seconds --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	SS
	call	LCD_Digit

	movf	Ones, w
	movwf	ss
	call	LCD_Digit	; Display SS

	call	DisplayDayOfWeek

	;;; Compensate for WWVB's phase signature ;;;

;	call	PhaseSigCheck	; Are we within the phase shift period now?
;	andlw	D'1'		; Test for a positive result.
;	btfss	STATUS, Z	; 'Z' is LOW if we're inside the period.
;	goto	in		; 'Z' is low.  Goto "in".
;
;out:
;	btfsc	rtc_status, 4	; Outside the period.  Check initial status.
;	goto	in_out		; Initially inside the period.  Now out.
;	goto	no_change	; Initially out.  Now out. (No change)
;
;in:
;	btfsc	rtc_status, 4	; Inside the period.  Check initial status.
;	goto	no_change	; Initially inside.  Now inside. (No change)
;	goto	out_in		; Initially outside.  Now inside.
;
;in_out:
;	bsf	PORTA, 6	; Introduce a +45 phase shift
;	bcf	PORTA, 7
;	goto	phase_end
;
no_change:
	bcf	PORTA, 6	; Introduce NO phase shift
	bcf	PORTA, 7	; Clear both bits
	goto	phase_end

;out_in:
;	bsf	PORTA, 6	; Introduce a -45 degree phase shift
;	bsf	PORTA, 7	; Set both bits HIGH

phase_end:

	;;; Continue with time/date display ;;;

	movlw	D'144'
	call	LCD_CMD
	movf	rtc_month, w	; rtc_month --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	MO
	call	LCD_Digit
	movf	Ones, w
	movwf	mo
	call	LCD_Digit	; Display month

	movlw	D'147'
	call	LCD_CMD
	movf	rtc_day, w	; rtc_day --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	DD
	call	LCD_Digit
	movf	Ones, w
	movwf	dd
	call	LCD_Digit	; Display day
	
	movlw	D'150'
	call	LCD_CMD
	movf	rtc_year, w	; rtc_year --> w
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w
	movwf	YY
	call	LCD_Digit
	movf	Ones, w
	movwf	yy
	call	LCD_Digit	; Display year

	movlw	D'19'		; Send Time/Date out via the serial port
	movwf	rscount

	bsf	STATUS, RP0		; Switch to Bank 1
	bsf	PIE1^0x080, TXIE	; Enable RS232 TX Interrupts
	bcf	STATUS, RP0		; Back to Bank 0

	movlw	D'212'		; UT1 sign  (+/-)
	call	LCD_CMD
	movlw	'+'
	btfsc	rtc_ut1, 7
	movlw	'-'
	call	LCD_Byte
	movf	rtc_ut1, w	; rtc_ut1 --> w
	andlw	D'127'		; mask out the high-order sign bit
	clrf	NumH
	movwf	NumL
	call	Bin2BCD
	movf	Tens, w		; Units (most likely a zero)
	call	LCD_Digit
	movlw	'.'		; decimal point
	call	LCD_Byte
	movf	Ones, w		; Fraction
	call	LCD_Digit

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Subroutine -- Display LCD messages  ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg1:
	movlw	D'128'		; Cursor home, first line
	call	LCD_CMD

	movlw	'W'		; "WWVB RCVR by KD2BD v1.0"
	call	LCD_Byte
	movlw	'W'
	call	LCD_Byte
	movlw	'V'
	call	LCD_Byte
	movlw	'B'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'R'
	call	LCD_Byte
	movlw	'C'
	call	LCD_Byte
	movlw	'V'
	call	LCD_Byte
	movlw	'R'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'b'
	call	LCD_Byte
	movlw	'y'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'K'
	call	LCD_Byte
	movlw	'D'
	call	LCD_Byte
	movlw	'2'
	call	LCD_Byte
	movlw	'B'
	call	LCD_Byte
	movlw	'D'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'v'
	call	LCD_Byte
	movlw	'1'
	call	LCD_Byte
	movlw	'.'
	call	LCD_Byte
	movlw	'0'
	call	LCD_Byte

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg2:
	movlw	D'128'		; Cursor home, first line
	call	LCD_CMD

	movlw	'W'		; "Waiting For Frame Start"
	call	LCD_Byte
	movlw	'a'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	movlw	't'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	movlw	'n'
	call	LCD_Byte
	movlw	'g'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'F'
	call	LCD_Byte
	movlw	'o'
	call	LCD_Byte
	movlw	'r'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'F'
	call	LCD_Byte
	movlw	'r'
	call	LCD_Byte
	movlw	'a'
	call	LCD_Byte
	movlw	'm'
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'S'
	call	LCD_Byte
	movlw	't'
	call	LCD_Byte
	movlw	'a'
	call	LCD_Byte
	movlw	'r'
	call	LCD_Byte
	movlw	't'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte

	movlw	D'192'		; Beginning of second line
	call	LCD_CMD

	movlw	'R'
	call	LCD_Byte
	movlw	'X'
	call	LCD_Byte
	movlw	':'
	call	LCD_Byte

	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg3:
	movlw	D'128'		; Cursor home, first line
	call	LCD_CMD

	movlw	'R'		; "Receiving Date/Time...  "
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	'c'
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	movlw	'v'
	call	LCD_Byte

date_time:
	movlw	'i'
	call	LCD_Byte
	movlw	'n'
	call	LCD_Byte
	movlw	'g'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'D'
	call	LCD_Byte
	movlw	'a'
	call	LCD_Byte
	movlw	't'
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	'/'
	call	LCD_Byte
	movlw	'T'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	movlw	'm'
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'T'
	call	LCD_Byte
	movlw	'-'
	call	LCD_Byte
	return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg4:
	movlw	D'128'		; Cursor home, first line
	call	LCD_CMD

	movlw	'V'		; "Verifying Date/Time...  "
	call	LCD_Byte
	movlw	'e'
	call	LCD_Byte
	movlw	'r'
	call	LCD_Byte
	movlw	'i'
	call	LCD_Byte
	movlw	'f'
	call	LCD_Byte
	movlw	'y'
	call	LCD_Byte
	goto	date_time

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

msg5:
	movlw	D'136'		; Screen setup for time/date display
	call	LCD_CMD
	movlw	' '
	call	LCD_Byte
	movlw	'o'
	call	LCD_Byte
	movlw	'n'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'/'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	'/'
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte
	movlw	' '
	call	LCD_Byte

	movlw	D'207'
	call	LCD_CMD
	movlw	'U'
	call	LCD_Byte
	movlw	'T'
	call	LCD_Byte
	movlw	'1'
	call	LCD_Byte
	movlw	':'
	call	LCD_Byte

	bsf	rtc_status, 7

	return

	end

